{% extends "console/layout.html" %}
{% set has_model = model is defined and model is not none %}
{% set mode_text = '"edit"' if has_model else '"create"' %}
{% set model_data = model if has_model else {} %}
{% block title %}{{ model_data.extension | default("New extension") }} · {{ site_name | default('RustPBX') }}{% endblock
%}
{% block content %}
{% set base_url = base_path | default('/console') %}
<div class="p-6" x-data='extensionDetailPage({
        basePath: {{ base_url | tojson }},
        mode: {{ mode_text }},
        model: {{ model_data | tojson }},
        departments: {{ filters.departments | default([]) | tojson }},
        assignedDepartments: {{ departments | default([]) | tojson }},
        locator: {{ registration_info | default({}) | tojson }}
    })' x-init="init()">
    <div class="mx-auto max-w-5xl space-y-6">
        <div class="flex flex-col gap-4 lg:flex-row lg:items-start lg:justify-between">
            <div class="space-y-3">
                <a :href="listUrl"
                    class="inline-flex items-center gap-2 text-sm font-medium text-slate-500 hover:text-slate-700">
                    <svg class="h-4 w-4" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="2">
                        <path stroke-linecap="round" stroke-linejoin="round" d="M12 5l-4 5 4 5" />
                    </svg>
                    Back to list
                </a>
                <div>
                    <h1 class="text-2xl font-semibold text-slate-900" x-text="pageTitle"></h1>
                    <p class="mt-2 text-sm text-slate-500"
                        x-text="mode === 'create' ? 'Define credentials, departments, and availability for the new extension.' : 'Update profile, credentials, routing, and access controls for this extension.'">
                    </p>
                </div>
            </div>
            <div class="rounded-xl bg-white p-5 text-sm text-slate-600 shadow-sm ring-1 ring-black/5">
                <div class="flex items-center justify-between">
                    <div class="font-semibold text-slate-900">Extension summary</div>
                    <template x-if="mode === 'edit'">
                        <span class="inline-flex items-center gap-1 rounded-full px-2.5 py-0.5 text-xs font-semibold"
                            :class="hasActiveRegistration()
                                ? 'bg-emerald-50 text-emerald-600 ring-1 ring-emerald-200'
                                : locator.error
                                    ? 'bg-rose-50 text-rose-600 ring-1 ring-rose-200'
                                    : 'bg-slate-100 text-slate-600 ring-1 ring-slate-200'">
                            <span class="h-1.5 w-1.5 rounded-full"
                                :class="hasActiveRegistration() ? 'bg-emerald-400' : locator.error ? 'bg-rose-400' : 'bg-slate-300'"></span>
                            <span
                                x-text="hasActiveRegistration() ? 'Active' : locator.error ? 'Unavailable' : 'Pending'"></span>
                        </span>
                    </template>
                </div>
                <dl class="mt-4 space-y-3 text-xs text-slate-500">
                    <div class="flex items-center justify-between gap-6">
                        <dt class="uppercase tracking-wide">Extension</dt>
                        <dd class="text-sm text-slate-700" x-text="form.extension || '—'"></dd>
                    </div>
                    <div class="flex items-center justify-between gap-6">
                        <dt class="uppercase tracking-wide">Created</dt>
                        <dd class="text-sm text-slate-700" x-text="formatDate(model?.created_at)"></dd>
                    </div>
                    <div class="flex items-center justify-between gap-6">
                        <dt class="uppercase tracking-wide">Last registered</dt>
                        <dd class="text-sm text-slate-700">
                            <div x-text="registrationStatusText()"></div>
                            <template x-if="locator.error">
                                <div class="text-xs text-rose-500" x-text="locator.error"></div>
                            </template>
                        </dd>
                    </div>
                    <div class="flex items-center justify-between gap-6">
                        <dt class="uppercase tracking-wide">Public access</dt>
                        <dd class="text-sm text-slate-700" x-text="form.public_call_allowed ? 'Allowed' : 'Restricted'">
                        </dd>
                    </div>
                </dl>
            </div>
        </div>

        <template x-if="success">
            <div class="rounded-lg border border-emerald-200 bg-emerald-50 px-4 py-3 text-sm text-emerald-700"
                x-text="success"></div>
        </template>
        <template x-if="error">
            <div class="rounded-lg border border-rose-200 bg-rose-50 px-4 py-3 text-sm text-rose-700" x-text="error">
            </div>
        </template>

        <form class="space-y-6" @submit.prevent="submit">
            <div class="space-y-6">
                <div class="rounded-xl bg-white p-3 shadow-sm ring-1 ring-black/5">
                    <nav class="inline-flex w-full flex-wrap gap-2 rounded-lg border border-slate-200 bg-slate-50 p-1 text-xs font-semibold text-slate-600"
                        role="tablist" aria-label="Extension configuration sections">
                        <template x-for="tab in tabs" :key="tab.id">
                            <button type="button" @click="setActiveTab(tab.id)"
                                class="flex-1 rounded-md px-4 py-2 text-left transition sm:flex-none"
                                :class="isActiveTab(tab.id) ? 'bg-white text-sky-700 shadow-sm' : 'text-slate-500 hover:text-slate-700'"
                                :aria-selected="isActiveTab(tab.id)" :aria-controls="`tab-panel-${tab.id}`">
                                <div class="flex flex-col">
                                    <span class="text-sm" x-text="tab.label"></span>
                                    <span class="text-[11px] font-normal text-slate-400"
                                        x-text="tab.description"></span>
                                </div>
                            </button>
                        </template>
                    </nav>
                </div>

                <div x-cloak x-show="activeTab === 'profile'" x-transition.opacity.duration.200ms
                    :id="`tab-panel-profile`" role="tabpanel" tabindex="0" class="space-y-6">
                    <div class="grid gap-6 md:grid-cols-2">
                        <label class="flex flex-col gap-2 text-sm font-medium text-slate-700">
                            Extension number
                            <input type="text" x-model.trim="form.extension" required maxlength="32"
                                class="rounded-lg border border-slate-200 px-3 py-2 text-sm text-slate-700 focus:border-sky-300 focus:outline-none focus:ring-2 focus:ring-sky-200"
                                placeholder="e.g. 1007">
                            <span class="text-xs font-normal text-slate-400">Numbers only. This becomes the SIP user ID
                                for
                                clients.</span>
                        </label>
                        <label class="flex flex-col gap-2 text-sm font-medium text-slate-700">
                            Display name
                            <input type="text" x-model.trim="form.display_name" maxlength="160"
                                class="rounded-lg border border-slate-200 px-3 py-2 text-sm text-slate-700 focus:border-sky-300 focus:outline-none focus:ring-2 focus:ring-sky-200"
                                placeholder="Visible to teammates and call logs">
                        </label>
                        <label class="flex flex-col gap-2 text-sm font-medium text-slate-700">
                            Email (optional)
                            <input type="email" x-model.trim="form.email" maxlength="160"
                                class="rounded-lg border border-slate-200 px-3 py-2 text-sm text-slate-700 focus:border-sky-300 focus:outline-none focus:ring-2 focus:ring-sky-200"
                                placeholder="name@example.com">
                        </label>
                        <label class="flex flex-col gap-2 text-sm font-medium text-slate-700">
                            SIP password
                            <input type="text" x-model.trim="form.sip_password" maxlength="160"
                                class="rounded-lg border border-slate-200 px-3 py-2 text-sm text-slate-700 focus:border-sky-300 focus:outline-none focus:ring-2 focus:ring-sky-200"
                                placeholder="Leave blank to keep current or auto-generate">
                            <span class="text-xs font-normal text-slate-400">Provide a strong password. Leave blank to
                                keep
                                the existing secret.</span>
                        </label>
                    </div>
                </div>

                <div x-cloak x-show="activeTab === 'access'" x-transition.opacity.duration.200ms
                    :id="`tab-panel-access`" role="tabpanel" tabindex="0" class="space-y-6">
                    <div class="rounded-xl bg-white p-6 shadow-sm ring-1 ring-black/5">
                        <h2 class="text-sm font-semibold text-slate-900">Departments</h2>
                        <p class="mt-1 text-xs text-slate-500">Assignments load once and remain available while editing.
                        </p>
                        <div class="mt-4 flex flex-wrap gap-2">
                            <template x-if="!availableDepartments.length">
                                <span class="rounded-full border border-slate-200 px-3 py-1 text-xs text-slate-400">No
                                    departments configured.</span>
                            </template>
                            <template x-for="dept in availableDepartments" :key="dept.id">
                                <label
                                    class="inline-flex items-center gap-2 rounded-full border border-slate-200 px-3 py-1 text-xs font-semibold text-slate-600 transition hover:border-sky-300 hover:text-sky-700">
                                    <input type="checkbox"
                                        class="h-4 w-4 rounded border-slate-300 text-sky-600 focus:ring-sky-400"
                                        :value="String(dept.id)" x-model="selectedDepartments">
                                    <span x-text="dept.name"></span>
                                </label>
                            </template>
                        </div>
                    </div>

                    <div class="grid gap-6 md:grid-cols-3">
                        <div class="space-y-3 rounded-xl bg-white p-6 shadow-sm ring-1 ring-black/5">
                            <div class="flex items-start justify-between">
                                <div>
                                    <h2 class="text-sm font-semibold text-slate-900">Portal login</h2>
                                    <p class="text-xs text-slate-500">Controls whether softphone or web clients can sign
                                        in.
                                    </p>
                                </div>
                                <button type="button" @click="form.login_allowed = !form.login_allowed"
                                    :class="form.login_allowed ? 'bg-emerald-100 text-emerald-600 ring-emerald-200' : 'bg-slate-100 text-slate-500 ring-slate-200'"
                                    class="inline-flex items-center gap-2 rounded-full px-3 py-1 text-xs font-semibold ring-2 transition">
                                    <svg class="h-3.5 w-3.5" viewBox="0 0 22 22" fill="none" stroke="currentColor"
                                        stroke-width="1.8">
                                        <path stroke-linecap="round" stroke-linejoin="round"
                                            d="M9 12.75 11.25 15 15 9.75m-3-7.036A11.959 11.959 0 0 1 3.598 6 11.99 11.99 0 0 0 3 9.749c0 5.592 3.824 10.29 9 11.623 5.176-1.332 9-6.03 9-11.622 0-1.31-.21-2.571-.598-3.751h-.152c-3.196 0-6.1-1.248-8.25-3.285Z" />
                                    </svg>
                                    <span x-text="form.login_allowed ? 'Enabled' : 'Disabled'"></span>
                                </button>
                            </div>
                        </div>
                        <div class="space-y-3 rounded-xl bg-white p-6 shadow-sm ring-1 ring-black/5">
                            <div class="flex items-start justify-between">
                                <div>
                                    <h2 class="text-sm font-semibold text-slate-900">Voicemail inbox</h2>
                                    <p class="text-xs text-slate-500">Allow unanswered calls to drop voicemail messages.
                                    </p>
                                </div>
                                <button type="button" @click="form.voicemail_enabled = !form.voicemail_enabled"
                                    :class="form.voicemail_enabled ? 'bg-sky-100 text-sky-600 ring-sky-200' : 'bg-slate-100 text-slate-500 ring-slate-200'"
                                    class="inline-flex items-center gap-2 rounded-full px-3 py-1 text-xs font-semibold ring-2 transition">
                                    <svg class="h-3.5 w-3.5" viewBox="0 0 22 22" fill="none" stroke="currentColor"
                                        stroke-width="1.8">
                                        <path stroke-linecap="round" stroke-linejoin="round"
                                            d="M2.25 13.5h3.86a2.25 2.25 0 0 1 2.012 1.244l.256.512a2.25 2.25 0 0 0 2.013 1.244h3.218a2.25 2.25 0 0 0 2.013-1.244l.256-.512a2.25 2.25 0 0 1 2.013-1.244h3.859m-19.5.338V18a2.25 2.25 0 0 0 2.25 2.25h15A2.25 2.25 0 0 0 21.75 18v-4.162c0-.224-.034-.447-.1-.661L19.24 5.338a2.25 2.25 0 0 0-2.15-1.588H6.911a2.25 2.25 0 0 0-2.15 1.588L2.35 13.177a2.25 2.25 0 0 0-.1.661Z" />
                                    </svg>
                                    <span x-text="form.voicemail_enabled ? 'Enabled' : 'Disabled'"></span>
                                </button>
                            </div>
                        </div>
                        <div class="space-y-3 rounded-xl bg-white p-6 shadow-sm ring-1 ring-black/5">
                            <div class="flex items-start justify-between">
                                <div>
                                    <h2 class="text-sm font-semibold text-slate-900">Public inbound calls</h2>
                                    <p class="text-xs text-slate-500">Allow anonymous SIP INVITEs to reach this
                                        extension.</p>
                                </div>
                                <button type="button" @click="form.public_call_allowed = !form.public_call_allowed"
                                    :class="form.public_call_allowed ? 'bg-emerald-100 text-emerald-600 ring-emerald-200' : 'bg-slate-100 text-slate-500 ring-slate-200'"
                                    class="inline-flex items-center gap-2 rounded-full px-3 py-1 text-xs font-semibold ring-2 transition">
                                    <svg class="h-3.5 w-3.5" viewBox="0 0 20 20" fill="none" stroke="currentColor"
                                        stroke-width="1.8">
                                        <path stroke-linecap="round" stroke-linejoin="round"
                                            d="M4.75 3.75h2.5l1 3-1.5.75a7 7 0 0 0 4.5 4.5l.75-1.5 3 1v2.5a1 1 0 0 1-1 1c-6.351 0-11.5-5.149-11.5-11.5a1 1 0 0 1 1-1Z" />
                                    </svg>
                                    <span x-text="form.public_call_allowed ? 'Allowed' : 'Restricted'"></span>
                                </button>
                            </div>
                            <p class="text-xs text-slate-500">Registration still requires credentials even when public
                                calling is enabled.</p>
                        </div>
                    </div>
                </div>

                <div x-cloak x-show="activeTab === 'routing'" x-transition.opacity.duration.200ms
                    :id="`tab-panel-routing`" role="tabpanel" tabindex="0" class="space-y-6">
                    <div class="space-y-4 rounded-xl bg-white p-6 shadow-sm ring-1 ring-black/5">
                        <header class="flex flex-col gap-1">
                            <h2 class="text-sm font-semibold text-slate-900">Call forwarding</h2>
                            <p class="text-xs text-slate-500">Configure destination and timeout. Disabled fields are
                                ignored
                                when forwarding is off.</p>
                        </header>
                        <div class="grid gap-3 md:grid-cols-2">
                            <template x-for="option in forwardingModes" :key="option.value">
                                <label
                                    class="flex cursor-pointer items-start gap-3 rounded-lg border px-4 py-3 text-sm transition"
                                    :class="form.call_forwarding_mode === option.value ? 'border-sky-200 bg-sky-50/60 shadow-sm' : 'border-slate-200 hover:border-slate-300'">
                                    <input type="radio"
                                        class="mt-1 h-4 w-4 border-slate-300 text-sky-600 focus:ring-sky-400"
                                        x-model="form.call_forwarding_mode" :value="option.value">
                                    <span>
                                        <span class="font-semibold text-slate-900" x-text="option.label"></span>
                                        <span class="block text-xs text-slate-500" x-text="option.description"></span>
                                    </span>
                                </label>
                            </template>
                        </div>
                        <div class="grid gap-5 md:grid-cols-2">
                            <label class="flex flex-col gap-2 text-sm font-medium text-slate-700">
                                Forward destination
                                <input type="text" x-model.trim="form.call_forwarding_destination" maxlength="160"
                                    :disabled="form.call_forwarding_mode === 'none'"
                                    :class="form.call_forwarding_mode === 'none' ? 'border-slate-200 bg-slate-50 text-slate-400 cursor-not-allowed' : 'border-slate-200 focus:border-sky-300 focus:ring-2 focus:ring-sky-200'"
                                    class="rounded-lg border px-3 py-2 text-sm text-slate-700"
                                    placeholder="SIP URI, PSTN number, or internal extension">
                                <span class="text-xs font-normal text-slate-400">Required whenever forwarding is
                                    enabled.</span>
                            </label>
                            <label class="flex flex-col gap-2 text-sm font-medium text-slate-700">
                                Ring timeout (seconds)
                                <input type="number" min="5" max="120" step="1"
                                    x-model.number="form.call_forwarding_timeout"
                                    :disabled="form.call_forwarding_mode === 'none'"
                                    :class="form.call_forwarding_mode === 'none' ? 'border-slate-200 bg-slate-50 text-slate-400 cursor-not-allowed' : 'border-slate-200 focus:border-sky-300 focus:ring-2 focus:ring-sky-200'"
                                    class="rounded-lg border px-3 py-2 text-sm text-slate-700">
                                <span class="text-xs font-normal text-slate-400">Accepted range: 5–120 seconds.</span>
                            </label>
                        </div>
                    </div>

                    <div class="rounded-xl bg-white p-6 shadow-sm ring-1 ring-black/5">
                        <h2 class="text-sm font-semibold text-slate-900">Internal notes</h2>
                        <textarea rows="4" x-model="form.notes"
                            class="mt-3 w-full rounded-lg border border-slate-200 px-3 py-2 text-sm text-slate-700 focus:border-sky-300 focus:outline-none focus:ring-2 focus:ring-sky-200"
                            placeholder="Optional notes for provisioning or troubleshooting."></textarea>
                    </div>
                </div>

                <div x-cloak x-show="activeTab === 'registrations'" x-transition.opacity.duration.200ms
                    :id="`tab-panel-registrations`" role="tabpanel" tabindex="0" class="space-y-6">
                    <div class="space-y-4 rounded-xl bg-white p-6 shadow-sm ring-1 ring-black/5">
                        <div class="flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between">
                            <div>
                                <h2 class="text-sm font-semibold text-slate-900">Active registrations</h2>
                                <p class="text-xs text-slate-500">Locator bindings currently associated with this
                                    extension.</p>
                            </div>
                            <template x-if="locator.query_uri">
                                <div
                                    class="inline-flex items-center gap-2 rounded-md bg-slate-100 px-2.5 py-1 text-[11px] font-medium text-slate-600 ring-1 ring-slate-200">
                                    <span class="uppercase tracking-wide text-slate-400">AoR</span>
                                    <span x-text="locator.query_uri"></span>
                                </div>
                            </template>
                        </div>

                        <template x-if="locator.error">
                            <div class="rounded-lg border border-rose-200 bg-rose-50 px-4 py-3 text-xs text-rose-600"
                                x-text="locator.error"></div>
                        </template>

                        <template x-if="locator.available && locator.records.length">
                            <div class="overflow-x-auto">
                                <table class="min-w-full divide-y divide-slate-200 text-left text-xs">
                                    <thead
                                        class="bg-slate-50 text-[11px] font-semibold uppercase tracking-wide text-slate-500">
                                        <tr>
                                            <th scope="col" class="px-3 py-2">Contact</th>
                                            <th scope="col" class="px-3 py-2">Destination</th>
                                            <th scope="col" class="px-3 py-2">Transport</th>
                                            <th scope="col" class="px-3 py-2">TTL</th>
                                            <th scope="col" class="px-3 py-2">Updated</th>
                                            <th scope="col" class="px-3 py-2">Flags</th>
                                            <th scope="col" class="px-3 py-2">User agent</th>
                                        </tr>
                                    </thead>
                                    <tbody class="divide-y divide-slate-100 text-slate-600">
                                        <template x-for="record in locator.records" :key="record.binding_key">
                                            <tr>
                                                <td class="px-3 py-2 align-top">
                                                    <div class="font-medium text-slate-900"
                                                        x-text="record.contact || record.registered_aor || record.aor">
                                                    </div>
                                                    <template x-if="record.binding_key">
                                                        <div class="text-[11px] text-slate-400"
                                                            x-text="record.binding_key"></div>
                                                    </template>
                                                </td>
                                                <td class="px-3 py-2 align-top">
                                                    <span x-text="record.destination || '—'"></span>
                                                </td>
                                                <td class="px-3 py-2 align-top">
                                                    <div class="flex flex-col gap-1">
                                                        <span x-text="record.transport || '—'"></span>
                                                        <template x-if="record.supports_webrtc">
                                                            <span
                                                                class="inline-flex w-max items-center rounded-full bg-emerald-50 px-2 py-0.5 text-[11px] font-medium text-emerald-600 ring-1 ring-emerald-100">
                                                                WebRTC
                                                            </span>
                                                        </template>
                                                    </div>
                                                </td>
                                                <td class="px-3 py-2 align-top">
                                                    <div class="font-semibold text-slate-900"
                                                        x-text="formatDuration(remainingTtl(record))"></div>
                                                    <div class="text-[11px] text-slate-400">Max
                                                        <span x-text="formatDuration(record.expires)"></span>
                                                    </div>
                                                </td>
                                                <td class="px-3 py-2 align-top">
                                                    <div class="text-sm font-semibold text-slate-700"
                                                        x-text="formatRelativeSeconds(record.age_seconds)"></div>
                                                </td>
                                                <td class="px-3 py-2 align-top">
                                                    <div class="flex flex-wrap gap-1">
                                                        <template x-for="flag in recordFlags(record)" :key="flag">
                                                            <span
                                                                class="inline-flex items-center rounded-full bg-slate-100 px-2 py-0.5 text-[11px] font-medium text-slate-600 ring-1 ring-slate-200"
                                                                x-text="flag"></span>
                                                        </template>
                                                        <template x-if="!recordFlags(record).length">
                                                            <span class="text-[11px] text-slate-400">—</span>
                                                        </template>
                                                    </div>
                                                </td>
                                                <td class="px-3 py-2 align-top">
                                                    <span class="block text-xs"
                                                        x-text="record.user_agent || '—'"></span>
                                                </td>
                                            </tr>
                                        </template>
                                    </tbody>
                                </table>
                            </div>
                        </template>

                        <template x-if="locator.available && !locator.records.length && !locator.error">
                            <div
                                class="rounded-lg border border-slate-200 bg-slate-50 px-4 py-3 text-xs text-slate-500">
                                No active registrations found for this extension.
                            </div>
                        </template>

                        <template x-if="!locator.available && !locator.error">
                            <div
                                class="rounded-lg border border-slate-200 bg-slate-50 px-4 py-3 text-xs text-slate-500">
                                Registration data will appear when the SIP server is connected and this extension has an
                                active binding.
                            </div>
                        </template>
                    </div>
                </div>
            </div>

            <div class="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
                <div class="text-xs text-slate-400">
                    Submissions update immediately through the API. All actions run via AJAX—no full page reloads
                    required.
                </div>
                <div class="flex items-center gap-3">
                    <a :href="listUrl"
                        class="inline-flex items-center justify-center rounded-lg border border-slate-200 px-4 py-2 text-sm font-semibold text-slate-600 transition hover:bg-slate-100">
                        Cancel
                    </a>
                    <button type="submit"
                        class="inline-flex items-center justify-center gap-2 rounded-lg bg-sky-600 px-5 py-2 text-sm font-semibold text-white shadow-sm transition hover:bg-sky-500 focus:outline-none focus:ring-2 focus:ring-sky-400 focus:ring-offset-2"
                        :disabled="saving">
                        <svg class="h-4 w-4 animate-spin" viewBox="0 0 20 20" fill="none" stroke="currentColor"
                            stroke-width="1.6" x-show="saving">
                            <path stroke-linecap="round" stroke-linejoin="round"
                                d="M4 10a6 6 0 0 1 6-6m0-3v3m6 6a6 6 0 0 1-6 6m0 3v-3m9-6h-3M1 10h3m10.95 4.95-2.12-2.12M4.05 5.05l2.12 2.12m0 5.66-2.12 2.12m9.9-9.9 2.12-2.12" />
                        </svg>
                        <span x-text="mode === 'create' ? 'Create extension' : 'Save changes'"></span>
                    </button>
                </div>
            </div>
        </form>
    </div>
</div>

<script>
    document.addEventListener('alpine:init', () => {
        Alpine.data('extensionDetailPage', (options) => {
            const normalizeLocator = (raw) => {
                if (!raw || typeof raw !== 'object') {
                    return {
                        available: false,
                        query_uri: null,
                        realm: null,
                        total: 0,
                        records: [],
                        error: null,
                    };
                }
                return {
                    available: !!raw.available,
                    query_uri: raw.query_uri || null,
                    realm: raw.realm || null,
                    total: Number(raw.total) || 0,
                    records: Array.isArray(raw.records) ? raw.records : [],
                    error: raw.error || null,
                };
            };

            return {
                basePath: options.basePath || '/console',
                mode: options.mode || 'create',
                model: options.model || {},
                availableDepartmentsRaw: options.departments || [],
                assignedDepartmentsRaw: options.assignedDepartments || [],
                locator: normalizeLocator(options.locator || options.registrations),
                listUrl: `${options.basePath || '/console'}/extensions`,
                success: null,
                error: null,
                saving: false,
                activeTab: 'profile',
                tabs: [
                    { id: 'profile', label: 'Profile', description: 'Extension details' },
                    { id: 'access', label: 'Access & Departments', description: 'Departments and access permissions' },
                    { id: 'routing', label: 'Call forwarding', description: 'Call forwarding and internal notes' },
                ],
                form: {
                    extension: options.model?.extension || '',
                    display_name: options.model?.display_name || '',
                    email: options.model?.email || '',
                    sip_password: '',
                    login_allowed: !(options.model?.login_disabled ?? false),
                    voicemail_enabled: !(options.model?.voicemail_disabled ?? false),
                    public_call_allowed: !!(options.model?.allow_guest_calls ?? false),
                    call_forwarding_mode: options.model?.call_forwarding_mode || 'none',
                    call_forwarding_destination: options.model?.call_forwarding_destination || '',
                    call_forwarding_timeout: options.model?.call_forwarding_timeout || 30,
                    notes: options.model?.notes || '',
                },
                selectedDepartments: [],
                forwardingModes: [
                    { value: 'none', label: 'No forwarding', description: 'Ring this extension only.' },
                    { value: 'always', label: 'Always forward', description: 'Forward every call immediately.' },
                    { value: 'when_busy', label: 'When busy', description: 'Forward only when already on a call.' },
                    { value: 'when_not_answered', label: 'When not answered', description: 'Forward if unanswered within the timeout.' },
                ],
                setActiveTab(tabId) {
                    const fallback = this.tabs[0]?.id || 'profile';
                    this.activeTab = this.tabs.some((tab) => tab.id === tabId) ? tabId : fallback;
                },
                isActiveTab(tabId) {
                    return this.activeTab === tabId;
                },
                get availableDepartments() {
                    return this.availableDepartmentsRaw.map((item) => ({
                        id: item.id,
                        name: item.name || item.display_name || item.label || `Department #${item.id}`,
                    }));
                },
                get pageTitle() {
                    return this.mode === 'edit'
                        ? `Edit ${this.form.display_name || this.form.extension || 'extension'}`
                        : 'Create extension';
                },
                submitUrl() {
                    if (this.mode === 'edit' && this.model?.id) {
                        return `${this.basePath}/extensions/${this.model.id}`;
                    }
                    return `${this.basePath}/extensions`;
                },
                submitMethod() {
                    return this.mode === 'edit' ? 'PATCH' : 'PUT';
                },
                init() {
                    this.selectedDepartments = (this.assignedDepartmentsRaw || [])
                        .map((dept) => String(dept.id))
                        .filter(Boolean);
                    this.form.call_forwarding_timeout = this.clampTimeout(this.form.call_forwarding_timeout);
                    if (this.mode === 'edit' && !this.tabs.some((tab) => tab.id === 'registrations')) {
                        this.tabs.push({
                            id: 'registrations',
                            label: 'Registrations',
                            description: 'Locator bindings and device presence',
                        });
                    }
                },
                clampTimeout(value) {
                    const numeric = Number(value);
                    if (!Number.isFinite(numeric)) {
                        return 30;
                    }
                    return Math.min(120, Math.max(5, Math.round(numeric)));
                },
                formatDate(value) {
                    if (!value) {
                        return '—';
                    }
                    const date = new Date(value);
                    if (Number.isNaN(date.getTime())) {
                        return value;
                    }
                    return date.toLocaleString();
                },
                remainingTtl(record) {
                    if (!record || typeof record !== 'object') {
                        return 0;
                    }
                    const expires = Number(record.expires) || 0;
                    const age = Number(record.age_seconds) || 0;
                    return Math.max(expires - age, 0);
                },
                formatDuration(value) {
                    const seconds = Math.max(Number(value) || 0, 0);
                    if (seconds < 1) {
                        return '0s';
                    }
                    if (seconds < 60) {
                        return `${Math.round(seconds)}s`;
                    }
                    const minutes = Math.floor(seconds / 60);
                    const remSeconds = Math.round(seconds % 60);
                    if (minutes < 60) {
                        return remSeconds
                            ? `${minutes}m ${remSeconds}s`
                            : `${minutes}m`;
                    }
                    const hours = Math.floor(minutes / 60);
                    const remMinutes = minutes % 60;
                    if (hours < 24) {
                        return remMinutes
                            ? `${hours}h ${remMinutes}m`
                            : `${hours}h`;
                    }
                    const days = Math.floor(hours / 24);
                    const remHours = hours % 24;
                    return remHours
                        ? `${days}d ${remHours}h`
                        : `${days}d`;
                },
                formatRelativeSeconds(value) {
                    const seconds = Number(value);
                    if (!Number.isFinite(seconds) || seconds <= 0) {
                        return 'moments ago';
                    }
                    if (seconds < 60) {
                        return `${Math.round(seconds)}s ago`;
                    }
                    if (seconds < 3600) {
                        const minutes = Math.floor(seconds / 60);
                        const rem = Math.round(seconds % 60);
                        return rem ? `${minutes}m ${rem}s ago` : `${minutes}m ago`;
                    }
                    if (seconds < 86400) {
                        const hours = Math.floor(seconds / 3600);
                        const remMinutes = Math.floor((seconds % 3600) / 60);
                        return remMinutes ? `${hours}h ${remMinutes}m ago` : `${hours}h ago`;
                    }
                    const days = Math.floor(seconds / 86400);
                    const remHours = Math.floor((seconds % 86400) / 3600);
                    return remHours ? `${days}d ${remHours}h ago` : `${days}d ago`;
                },
                recordFlags(record) {
                    if (!record || typeof record !== 'object') {
                        return [];
                    }
                    const flags = [];
                    if (record.supports_webrtc) {
                        flags.push('WebRTC');
                    }
                    if (record.gruu) {
                        flags.push('GRUU');
                    }
                    if (record.temp_gruu) {
                        flags.push('Temp GRUU');
                    }
                    if (Array.isArray(record.path) && record.path.length) {
                        flags.push('Path');
                    }
                    if (Array.isArray(record.service_route) && record.service_route.length) {
                        flags.push('Service Route');
                    }
                    if (record.instance_id) {
                        flags.push('Instance');
                    }
                    return flags;
                },
                hasActiveRegistration() {
                    return !!(this.locator?.available && Array.isArray(this.locator.records) && this.locator.records.length);
                },
                firstRegistration() {
                    if (!this.hasActiveRegistration()) {
                        return null;
                    }
                    return this.locator.records[0];
                },
                registrationStatusText() {
                    const record = this.firstRegistration();
                    if (!record) {
                        if (this.model?.registered_at) {
                            return `Last seen ${this.formatDate(this.model.registered_at)}`;
                        }
                        return 'Awaiting registration';
                    }
                    const ttl = this.formatDuration(this.remainingTtl(record));
                    const seen = this.formatRelativeSeconds(record.age_seconds);
                    return `${seen} • TTL ${ttl}`;
                },
                buildPayload() {
                    return {
                        'extension': this.form.extension.trim(),
                        'display_name': this.form.display_name.trim(),
                        'email': this.form.email.trim(),
                        'sip_password': this.form.sip_password.trim() || undefined,
                        'login_disabled': this.form.login_allowed ? false : true,
                        'voicemail_disabled': this.form.voicemail_enabled ? false : true,
                        'allow_guest_calls': this.form.public_call_allowed === true,
                        'call_forwarding_mode': this.form.call_forwarding_mode || 'none',
                        'call_forwarding_destination': this.form.call_forwarding_mode !== 'none' && this.form.call_forwarding_destination.trim()
                            ? this.form.call_forwarding_destination.trim()
                            : undefined,
                        'call_forwarding_timeout': this.form.call_forwarding_mode !== 'none'
                            ? this.clampTimeout(this.form.call_forwarding_timeout)
                            : undefined,
                        'notes': this.form.notes ? this.form.notes.trim() : '',
                        'department_ids': this.selectedDepartments.map((id) => id.trim()).filter(Boolean),
                    }
                },
                async submit() {
                    if (!this.form.extension.trim()) {
                        this.error = 'Extension number is required.';
                        return;
                    }
                    if (this.form.call_forwarding_mode !== 'none' && !this.form.call_forwarding_destination.trim()) {
                        this.error = 'Forwarding destination is required when call forwarding is enabled.';
                        return;
                    }

                    this.saving = true;
                    this.error = null;
                    this.success = null;
                    try {
                        const body = this.buildPayload();
                        const response = await fetch(this.submitUrl(), {
                            method: this.submitMethod(),
                            headers: {
                                'Accept': 'application/json',
                                'Content-Type': 'application/json',
                            },
                            body: JSON.stringify(body),
                        });
                        const data = await response.json().catch(() => ({}));
                        if (!response.ok) {
                            throw new Error(data?.message || 'Failed to save extension');
                        }

                        this.success = this.mode === 'create'
                            ? 'Extension created successfully. Returning to the list...'
                            : 'Changes saved. Returning to the list...';
                        setTimeout(() => {
                            window.location.href = this.listUrl;
                        }, 600);
                    } catch (err) {
                        console.error(err);
                        this.error = err.message || 'Failed to submit form';
                    } finally {
                        this.saving = false;
                    }
                },
            };
        });
    });
</script>

{% endblock %}